import { JSEncrypt } from 'jsencrypt'

class JsEncryptNew extends JSEncrypt {
  public b64map: string
  public b64pad: string
  public BI_RM: string

  constructor() {
    super()
    this.b64map = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
    this.b64pad = '='
    this.BI_RM = '0123456789abcdefghijklmnopqrstuvwxyz'
  }

  public int2char(n: number) {
    return this.BI_RM.charAt(n)
  }

  public b64tohex(s: string) {
    let ret = ''
    let i
    let k = 0 // b64 state, 0-3
    let slop = 0
    for (i = 0; i < s.length; ++i) {
      if (s.charAt(i) === this.b64pad) {
        break
      }
      const v = this.b64map.indexOf(s.charAt(i))
      if (v < 0) {
        continue
      }
      if (k === 0) {
        ret += this.int2char(v >> 2)
        slop = v & 3
        k = 1
      } else if (k === 1) {
        ret += this.int2char((slop << 2) | (v >> 4))
        slop = v & 0xf
        k = 2
      } else if (k === 2) {
        ret += this.int2char(slop)
        ret += this.int2char(v >> 2)
        slop = v & 3
        k = 3
      } else {
        ret += this.int2char((slop << 2) | (v >> 4))
        ret += this.int2char(v & 0xf)
        k = 0
      }
    }
    if (k === 1) {
      ret += this.int2char(slop << 2)
    }
    return ret
  }

  public hex2b64(h: string) {
    let i
    let c
    let ret = ''
    for (i = 0; i + 3 <= h.length; i += 3) {
      c = parseInt(h.substring(i, i + 3), 16)
      ret += this.b64map.charAt(c >> 6) + this.b64map.charAt(c & 63)
    }
    if (i + 1 === h.length) {
      c = parseInt(h.substring(i, i + 1), 16)
      ret += this.b64map.charAt(c << 2)
    } else if (i + 2 === h.length) {
      c = parseInt(h.substring(i, i + 2), 16)
      ret += this.b64map.charAt(c >> 2) + this.b64map.charAt((c & 3) << 4)
    }
    while ((ret.length & 3) > 0) {
      ret += this.b64pad
    }
    return ret
  }

  /**
   * 分段加密
   * @param {string} string 要加密的字符串
   * @returns 加密后的 base64 字符串 | false
   */
  public encryptLong(string: string) {
    const k = this.getKey()

    try {
      let ct = ''
      // RSA每次加密117bytes，需要辅助方法判断字符串截取位置
      // 1.获取字符串截取点
      const bytes: Array<number> = []
      bytes.push(0)
      let byteNo = 0
      let len, c
      len = string.length
      let temp = 0
      for (let i = 0; i < len; i++) {
        c = string.charCodeAt(i)
        if (c >= 0x010000 && c <= 0x10FFFF) {
          byteNo += 4
        } else if (c >= 0x000800 && c <= 0x00FFFF) {
          byteNo += 3
        } else if (c >= 0x000080 && c <= 0x0007FF) {
          byteNo += 2
        } else {
          byteNo += 1
        }
        if ((byteNo % 117) >= 114 || (byteNo % 117) === 0) {
          if (byteNo - temp >= 114) {
            bytes.push(i)
            temp = byteNo
          }
        }
      }
      // 2.截取字符串并分段加密
      if (bytes.length > 1) {
        for (let i = 0; i < bytes.length - 1; i++) {
          let str
          if (i === 0) {
            str = string.substring(0, bytes[i + 1] + 1)
          } else {
            str = string.substring(bytes[i] + 1, bytes[i + 1] + 1)
          }
          const t1 = k.encrypt(str)
          ct += t1
        }
        ;
        if (bytes[bytes.length - 1] !== string.length - 1) {
          const lastStr = string.substring(bytes[bytes.length - 1] + 1)
          ct += k.encrypt(lastStr)
        }
        return this.hex2b64(ct)
      }
      const t = k.encrypt(string)
      return this.hex2b64(t)

    } catch (ex) {
      return false
    }
  }
  /**
   * 分段解密长字符串
   * @param text 要解秘的字符串
   * @returns 解密后的字符串
   */
  public decryptLong(text: string) {
    const k = this.getKey()

    const maxLength = (((k as any).n.bitLength() + 7) >> 3)
    try {
      const str = this.b64tohex(text)

      const inputLen = str.length

      let ct = ''

      if (inputLen > maxLength) {

        const lt = str.match(/.{1,256}/g)
        lt?.forEach(function (entry) {
          const t1 = k.decrypt(entry)
          ct += t1
        })
        return ct
      }

      return k.decrypt(this.b64tohex(text))
    } catch (ex) {
      return false
    }
  }
}

export default JsEncryptNew